/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

import fse from 'fs-extra';

import { CacheService } from '../../internal/cache/service/cache-service.js';
import { HvigorLogger } from '../../log/hvigor-log.js';
import { LockOptions, lockSync, unlockSync } from '../../util/file-lock.js';
import { DAEMON_INFO_IN_REGISTRY_MAX_LIMIT } from '../const/daemon-const.js';
import { isHvigorDaemonProcess } from '../util/process-info-util.js';

import { DaemonInfo, DaemonState } from './daemon-info.js';

const DEFAULT_LOCK_OPTION: LockOptions = {
  retries: 100,
  update: 1000,
};

// 每个写动作每次都需要落盘，才能用文件锁来保证
export class DaemonCacheService implements CacheService<DaemonInfo> {
  private _log: HvigorLogger;
  private readonly cachePath: string;

  constructor(cachePath: string, log: HvigorLogger) {
    this.cachePath = cachePath;
    if (!fse.existsSync(cachePath)) {
      fse.ensureFileSync(cachePath);
      fse.writeJsonSync(cachePath, {});
    }
    this._log = log;
  }

  set(key: string, entryContent: DaemonInfo): void {
    lockSync(this.cachePath, DEFAULT_LOCK_OPTION);
    const daemonRegistry = this.getAll();
    daemonRegistry[key] = {
      ...entryContent,
      lastUsedTime: Date.now(),
    };
    fse.writeJsonSync(this.cachePath, daemonRegistry);
    unlockSync(this.cachePath);
  }

  close(): void {
    // do nothing
  }

  get(key: string): DaemonInfo {
    const daemonRegistry = this.getAll();
    return daemonRegistry[key] || {};
  }

  remove(key: string): void {
    lockSync(this.cachePath, DEFAULT_LOCK_OPTION);
    const daemonRegistry = this.getAll();
    delete daemonRegistry[key];
    fse.writeJsonSync(this.cachePath, daemonRegistry);
    unlockSync(this.cachePath);
  }

  size(): number {
    return Object.entries(fse.readJsonSync(this.cachePath)).length;
  }

  initialize(): void {
    // do nothing
  }

  getAll(): { [keyId: string]: DaemonInfo } {
    try {
      const daemonRegistry: { [keyId: string]: DaemonInfo } = fse.readJsonSync(this.cachePath);

      // 当数量超过一定阈值时，清理stopped和broken状态的daemonInfo
      if (Object.entries(daemonRegistry).length > DAEMON_INFO_IN_REGISTRY_MAX_LIMIT) {
        this._log.debug('Daemon info over limit. Clean daemon info registry.');
        Object.entries(daemonRegistry).forEach(([key, daemonInfo]) => {
          if (daemonInfo.state === DaemonState.BROKEN || daemonInfo.state === DaemonState.STOPPED) {
            this.removeUselessInfoInDaemonRegistry(daemonInfo, daemonRegistry, key);
          }
        });
      }
      return daemonRegistry;
    } catch (e) {
      this._log.debug(e);
      this._log.debug('The registry information is abnormal, the daemon information will be cleared.');
      fse.removeSync(this.cachePath);
      fse.writeJsonSync(this.cachePath, {});
      return {};
    }
  }

  private removeUselessInfoInDaemonRegistry(uselessDaemonInfo: DaemonInfo, daemonRegistry: { [p: string]: DaemonInfo }, daemonInfoKey: string) {
    const removeDaemonPid = uselessDaemonInfo.pid;

    // 检查该daemon process是否还存在,如果还存在,判断是否是hvigor daemon进程
    if (isHvigorDaemonProcess(removeDaemonPid)) {
      // 状态已为stopped和broken,但是进程还存在,则需要再发送信号确保去关闭该进程
      process.kill(removeDaemonPid, 'SIGKILL');
    } else {
      this._log.debug(`Client check process is not a hvigor daemon or not exist. daemonPid=${removeDaemonPid}`);
    }
    this._log.debug('Remove daemon info. The information in the registry has exceeded the limit.' + `removedDaemonInfo=${uselessDaemonInfo}`);
    delete daemonRegistry[daemonInfoKey];
  }
}
